package evolution.algorithm;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import evolution.executor.EvolutionaryAlghorithmEpochRunner;
import evolution.individual.Individual;
import evolution.interceptor.CoevolutionIterationInterceptor;
import evolution.operation.migration.MigrationStrategy;
import evolution.stop.StopCondition;

public class CoevolutionAlgorithm implements Runnable, Iterative {

	private List<EvolutionaryAlghorithmEpochRunner> evolutionaryAlghorithmEpochRunners;
	private StopCondition<? super CoevolutionAlgorithm> stopCondition;
	private MigrationStrategy migrationStrategy;
	private int currentEpoch = 0;
	private ExecutorService threadPool;
	private CoevolutionIterationInterceptor interceptor;

	@Override
	public void run() {
		int populations = evolutionaryAlghorithmEpochRunners.size();
		threadPool = Executors.newFixedThreadPool(populations);

		while (!stopCondition.shouldStop(this)) {
			runIteration();
		}
		threadPool.shutdown();

		if (interceptor != null) {
			interceptor.finish(this);
		}
	}

	@Override
	public void runIteration() {
		if (interceptor != null) {
			interceptor.intercept(this);
		}
		currentEpoch++;
		try {
			threadPool.invokeAll(evolutionaryAlghorithmEpochRunners);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		migrationStrategy.migrate(this);

	}

	public List<EvolutionaryAlghorithmEpochRunner> getEvolutionaryAlghorithmEpochRunners() {
		return evolutionaryAlghorithmEpochRunners;
	}

	public void setEvolutionaryAlghorithmEpochRunners(
			List<EvolutionaryAlghorithmEpochRunner> evolutionaryAlghorithmEpochRunners) {
		this.evolutionaryAlghorithmEpochRunners = evolutionaryAlghorithmEpochRunners;
	}

	public MigrationStrategy getMigrationStrategy() {
		return migrationStrategy;
	}

	public void setMigrationStrategy(MigrationStrategy migrationStrategy) {
		this.migrationStrategy = migrationStrategy;
	}

	@Override
	public int getCurrentIteration() {
		return currentEpoch;
	}

	public StopCondition<? super CoevolutionAlgorithm> getStopCondition() {
		return stopCondition;
	}

	public void setStopCondition(
			StopCondition<? super CoevolutionAlgorithm> stopCondition) {
		this.stopCondition = stopCondition;
	}

	public CoevolutionIterationInterceptor getInterceptor() {
		return interceptor;
	}

	public void setInterceptor(CoevolutionIterationInterceptor interceptor) {
		this.interceptor = interceptor;
	}

	public Individual getBestIndividual() {

		int size = evolutionaryAlghorithmEpochRunners.size();
		Individual best = evolutionaryAlghorithmEpochRunners.get(0)
				.getEvolutionaryAlghorithm().getPopulation()[0];

		for (int i = 1; i < size; i++) {
			Individual current = evolutionaryAlghorithmEpochRunners.get(i)
					.getEvolutionaryAlghorithm().getPopulation()[0];

			if (current.compareTo(best) > 0) {
				best = current;
			}
		}

		return best;
	}
}
